#ifndef __SERVER_H__
#define __SERVER_H__

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <list>
// for int conversion
#include <sstream>
#include <iostream>

#include <unistd.h>

#include "Dispatch.h"

#define MAX_CONNECTIONS 10
#define CORRUPT_CLIENT_THRESHOLD 10

class Server {
public:
	Server(unsigned int, unsigned int, char* const*, bool);
	~Server( void );
	
	// Main run function for the server.
	void run();
	
	// Can be set to daemon mode so can be run in the background.
	bool daemon;
	
	// Print only if not in daemon mode
	inline void ifprintf(const char *message, ...) { if(!daemon) { va_list args; va_start(args, message); vfprintf(stdout, message, args); va_end(args); } }
	
private:
	
	typedef struct {
		std::string username;
		// Also used as Unique Client ID
		uint16_t chat_socket;
		
		// UDP socket address information for NAT Punchthrough.
		struct sockaddr_in udp;
		
		// Indicates whether user has completed connection process
		bool connected;
		
		// Keeps track of the number of corrupt messages
		// the server recieves from the client. Too many
		// will result in the server dropping the client.
		unsigned int corrupt;
		
	} ClientConnection;
	
	struct sockaddr_in chat_socket_addr;
	
	// To listening socket to which clients initially connect.
	int chat_connect_socket;
	int file_connect_socket;
	
	// File Descriptor set for client chat connections
	fd_set client_set;
	
	// List of client connections
	std::list<ClientConnection*> clients;
	
	ClientConnection *findClientByUID( uint16_t c_id );
	ClientConnection *findClientByUsername( std::string c_name );
	
	
	// Maximum file descriptor. Used in multiplexing I/O
	int max_fd;
	
	unsigned int maxConnects;
	unsigned int chat_port;
	
	
	/* Deals with a clients file transfer handshaking.
	 ***************************************************/
	void processFileTransferMessages( ClientConnection *from, Dispatch *d, DispatchFlag flag );
	
	/* Responsible for taking a socket and creating
	 * a new ClientConnection.
	 ***************************************************/
	void acceptConnection( int socket );
	
	/* Takes a Dispatch pointer and sends it to all
	 * the clients in the servers client list (clients)
	 * Will exclude the ClientConnection if specified
	 ***************************************************/
	void notifyClients( Dispatch *dispatch, ClientConnection* exclude = NULL );
	
	/* Generates a dispatch containing the list of users
	 * and sends to all connected clients
	 ***************************************************/
	void notifyClientsUserList( void );
	
	/* Informs particular client of its uid and username
	 ***************************************************/
	void dictateClient( ClientConnection *client );
	
	/* Checks existing users for conflicting username.
	 * If no conflict, sets and notifies client of name change
	 * RETURN: whether or not the requested change was accepted.
	 ***********************************************************/
	bool setClientUsername( ClientConnection *client, std::string username );
	
	void handleClientMessage( ClientConnection *client, Dispatch *d );
	
	/* Completes the client connection process.
	 * Notifies other users of new user.
	 ***********************************************************/
	void completeClientConnection( ClientConnection *client, std::string username );
	
	/* Disconnects client. Closes socket, and notifies
	 * all other clients using the most appropriate
	 * DispatchFlag (See Dispatch.h).
	 * DOES NOT REMOVE CLIENT FROM clients LIST!
	 ***************************************************/
	void disconnectClient( ClientConnection* client );
	
	/* Cycle through all clients processing each message
	 ***************************************************/
	void processCommunicationFromTCP( fd_set readfrom );
	
	/* Sets the maximum File Descriptor from the list
	 * of clients currently connected to the server
	 ***************************************************/
	void setMaxFD(void);
	
	/* Various forms of initializing the server.
	 * If parameters are not specified, defaults are
	 * used instead. See top of Server.h
	 ***************************************************/
	void init(void);
	void init(unsigned int max);
	void init(unsigned int max, unsigned int port);
	
	void init_tcp( void );
	
public:
	
	/* Returns a comma seperated list of users (usernames)
	 * excluding a particular client if specified.
	 ****************************************************/
	std::string listUsers( ClientConnection *exclude = NULL );
};

#endif
